package com.jt.helloworld.designpattern.singleton;

/**
 * 单例模式
 * 属于创建型模式，它提供了一种创建对象的最佳方式
 * tips:
 * 1、单例类只能有一个实例；
 * 2、单例类必须自己创建自己的唯一实例；
 * 3、单例类必须给所有其他对象提供这一实例。
 */
public class Singleton {

    public static void main(String[] args) {
        Singleton01 singleton01_1 = Singleton01.getInstance();
        Singleton01 singleton01_2 = Singleton01.getInstance();
        System.out.println("饿汉模式：" + (singleton01_1 == singleton01_2));

        Singleton02 singleton02_1 = Singleton02.getInstance();
        Singleton02 singleton02_2 = Singleton02.getInstance();
        System.out.println("懒汉模式-线程不安全：" + (singleton02_1 == singleton02_2));

    }
}

/**
 * 单例模式-饿汉式（静态变量）
 *
 * 是否 Lazy 初始化：否
 *
 * 是否多线程安全：是
 *
 * 实现难度：易
 *
 * 描述：这种方式比较常用，但容易产生垃圾对象。
 * 优点：没有加锁，执行效率会提高。
 * 缺点：类加载时就初始化，浪费内存。
 * 它基于 classloader 机制避免了多线程的同步问题，不过，instance 在类装载时就实例化，虽然导致类装载的原因有很多种，在单例模式中大多数都是调用 getInstance 方法， 但是也不能确定有其他的方式（或者其他的静态方法）导致类装载，这时候初始化 instance 显然没有达到 lazy loading 的效果。
 */
class Singleton01 {

    // 1.本类内部创建对象实例
    private static Singleton01 instance = new Singleton01();

    // 2.构造器私有化，外部不能new
    private Singleton01() {

    }

    // 3.提供一个公有的静态方法，返回实例
    public static Singleton01 getInstance(){
        return instance;
    }
}

/**
 * 懒汉式，线程不安全
 *
 * 是否 Lazy 初始化：是
 *
 * 是否多线程安全：否
 *
 * 实现难度：易
 *
 * 描述：这种方式是最基本的实现方式，这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized，所以严格意义上它并不算单例模式。
 * 这种方式 lazy loading 很明显，不要求线程安全，在多线程不能正常工作。
 */
class Singleton02{
    private static Singleton02 instance;
    private Singleton02() {

    }
    public static Singleton02 getInstance(){
        if(instance == null){
            instance = new Singleton02();
        }
        return instance;
    }

}

/**
 * 懒汉式，线程安全
 *
 * 是否 Lazy 初始化：是
 *
 * 是否多线程安全：是
 *
 * 实现难度：易
 *
 * 描述：这种方式具备很好的 lazy loading，能够在多线程中很好的工作，但是，效率很低，99% 情况下不需要同步。
 * 优点：第一次调用才初始化，避免内存浪费。
 * 缺点：必须加锁 synchronized 才能保证单例，但加锁会影响效率。
 * getInstance() 的性能对应用程序不是很关键（该方法使用不太频繁）
 */
class Singleton03{
    private static Singleton03 instance;
    private Singleton03() {

    }

    public static synchronized Singleton03 getInstance(){
        if(instance == null) {
            instance = new Singleton03();
        }
        return instance;
    }
}

/**
 * 双检锁/双重校验锁（DCL，即 double-checked locking）
 *
 * JDK 版本：JDK1.5 起
 *
 * 是否 Lazy 初始化：是
 *
 * 是否多线程安全：是
 *
 * 实现难度：较复杂
 *
 * 描述：这种方式采用双锁机制，安全且在多线程情况下能保持高性能。
 */
class Singleton04{
    private volatile static Singleton04 instance;
    private Singleton04(){

    }

    public static Singleton04 getInstance(){
        if(instance == null) {
            synchronized (Singleton04.class){
                if(instance == null) {
                    instance = new Singleton04();
                }
            }
        }
        return instance;
    }
}

/**
 * 登记式/静态内部类
 *
 * 是否 Lazy 初始化：是
 *
 * 是否多线程安全：是
 *
 * 实现难度：一般
 *
 * 描述：这种方式能达到双检锁方式一样的功效，但实现更简单。对静态域使用延迟初始化，应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况，双检锁方式可在实例域需要延迟初始化时使用。
 * 这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程，它跟第 3 种方式不同的是：第 3 种方式只要 Singleton 类被装载了，那么 instance 就会被实例化（没有达到 lazy loading 效果），而这种方式是 Singleton 类被装载了，instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用，只有通过显式调用 getInstance 方法时，才会显式装载 SingletonHolder 类，从而实例化 instance。想象一下，如果实例化 instance 很消耗资源，所以想让它延迟加载，另外一方面，又不希望在 Singleton 类加载时就实例化，因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载，那么这个时候实例化 instance 显然是不合适的。这个时候，这种方式相比第 3 种方式就显得很合理。
 */
class Singleton05{

    // Singleton05类被装载时，静态内部类SingletonHolder没有被装载
    private static class SingletonHolder {
        private static final Singleton05 INSTANCE = new Singleton05();
    }
    private Singleton05() {

    }

    // 当调用getInstance()时，才会装载静态内部类SingletonHolder，装载类是线程安全的（JVM保证 ）
    public static final Singleton05 getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

/**
 * 枚举
 *
 * 是否 Lazy 初始化：否
 *
 * 是否多线程安全：是
 *
 * 实现难度：易
 *
 * 描述：这种实现方式还没有被广泛采用，但这是实现单例模式的最佳方法。它更简洁，自动支持序列化机制，绝对防止多次实例化。
 */
enum Singleton06{
    INSTANCE;

    public void sayOK() {
        System.out.println("ok! ");
    }
}
